#ifndef TASKMANAGER_HPP
#define TASKMANAGER_HPP
#include "graph/graph.hpp"
#include "queueSet.hpp"
#include "queue.hpp"
#include "task.hpp"

#include <condition_variable>
#include <thread>
#include <vector>
#include <fstream>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
#include <cstring>

class TaskManager {
    class Terminator {
        public:
            Terminator() : _offered_termination(0)
                         , _spin_master(nullptr) {}
            ~Terminator(){}
        private:
            std::mutex _lock;
            std::condition_variable cv;
            volatile int _offered_termination;
            std::thread* _spin_master; //TODO: use spin master to teminate

        public:
            bool offer_termination();
            void prepare_for_return();
    };
public:
    static QueueSet* _qs;
    static Terminator* terminator;
    static std::vector<TaskManager*> workers;
    static int worker_num;
    static void initialize(Graph &graph);
    static void end();
    static void start(TaskManager* tm);

    TaskManager(int id, Queue* q): _id(id), task_stack(q), processNum(0) {
        int MAX_PATH = 256;
        char* outputFileName = new char[MAX_PATH];
        getcwd(outputFileName, MAX_PATH);
        sprintf(outputFileName + strlen(outputFileName), "/%d.out", id);
        output.open(outputFileName);
    }
    ~TaskManager();

private:
    int _id;
    std::thread* _t;
    Queue* task_stack;
    std::ofstream output;
    int processNum;
    void work();
    void drain_stack();
    bool offer_termination();

public:
    bool steal(int id, Task* &t);
    void push(Task* task);
    void set_thread(std::thread* t) { _t = t; }
    void set_queue(Queue* q) { task_stack = q; }
    std::thread* get_thread() {return _t;}
    void pushGCRoot(Graph &graph, int start, int taskNumber);

    std::ofstream& getOutput();
    int getId();
    void addProcessNum();
    int getProcessNum();
};

#endif
